Skip to content

feat: hb_device reserved keys - #1077

Open
charmful0x wants to merge 2 commits into
edgefrom
feat/device-reserved
Open

feat: hb_device reserved keys#1077
charmful0x wants to merge 2 commits into
edgefrom
feat/device-reserved

Conversation

@charmful0x

Copy link
Copy Markdown

this PR add hb_device:is_reserved/3,4: a key is reserved if the device declares it in info().reserved or it resolves to a real exported device function

it lets callers validate user keys against the actual device namespace, so trie@1.0 rejects internals like node-value and callable keys like get/set without having a hardcoded ?RESERVED_KEYS macro (removed in this PR)

Comment thread src/core/device/hb_device.erl Outdated

%% @doc Check if a key is claimed by the device's direct function dispatch.
is_device_key(Dev, Base, Key, Opts) ->
case message_to_fun(Dev, Base, Key, Opts) of

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should handle the case where there is no match? Conditions for that to happen the device author would need to halt the device from defaulting to ~message@1.0 and also not implement a catch all handler themselves.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an issue where info_handler_to_fun calls message_to_fun, which returns a tuple with 3 values, but the result is expected to be a tuple with 2 values.

{Status, Func} = info_handler_to_fun(Handler, Msg, Key, Opts),

I will fix this and also catch the throw to avoid the infinity loop.

is_exported(_Info, _Key, _Opts) -> true.

%% @doc Determine if a key is reserved by a device.
%% A key is reserved if it is declared in the device's `info().reserved' list,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems OK but its a kind of strange concept when you think about it... What exactly does it mean for the key to be reserved vs exported? We don't expect there to be any other protocol rules, but your idea is to have a way for a message's device to signal generically that while a key not be computed it... Should also not be set by the user? But it may be found as raw data because the device itself can/will set it? In which case it is part of the device's public interface -- just not computed directly?

If so, I think I grok it. Comment in that case is technically correct but maybe we should add a tiny sentence explaining 'this allows device authors to signal generically to other callers that a given key in a message is anticipated to be used for the device's internal purposes, whether or not its value is defined by Erlang function calls or message@1.0-inherited literal lookups.'?

Comment on lines +333 to +362
explicit_reserved_key_test() ->
Trie = #{ <<"device">> => <<"trie@1.0">> },
?assert(is_reserved(Trie, <<"node-value">>, #{})),
?assert(is_reserved(<<"trie@1.0">>, Trie, <<"node-value">>, #{})),
?assert(is_reserved(Trie, <<"Node-Value">>, #{})),
?assertNot(is_reserved(Trie, <<"alice">>, #{})).

message_reserved_key_test() ->
Msg = #{ <<"device">> => <<"message@1.0">> },
Trie = #{ <<"device">> => <<"trie@1.0">> },
?assert(is_reserved(Msg, <<"path">>, #{})),
?assert(is_reserved(Msg, <<"set">>, #{})),
?assert(is_reserved(Msg, <<"get">>, #{})),
?assert(is_reserved(Trie, <<"get">>, #{})),
?assert(is_reserved(Trie, <<"set">>, #{})),
?assert(is_reserved(Trie, <<"keys">>, #{})),
?assertNot(is_reserved(Trie, <<"commit">>, #{})),
?assertNot(is_reserved(Msg, <<"alice">>, #{})).

trie_keys_skip_reserved_keys_test() ->
Trie =
#{
<<"device">> => <<"trie@1.0">>,
<<"node-value">> => ignored,
<<"get">> => ignored,
<<"set">> => ignored,
<<"keys">> => ignored,
<<"alice">> => 1
},
?assertEqual([<<>>, <<"alice">>], lists:sort(hb_ao:keys(Trie, #{}))).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a nit but mind using ~test-device@1.0 for this please? We might move the trie@1.0 out of the preloaded devices sometime.

<<"verify">>
]).

%% The list of keys that the message device reserves at protocol level.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this shouldn't be needed? These should all be returned as true by is_reserved anyway?

<<"node-value">>,
<<"device">>,
<<"commitments">>,
<<"priv">>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be an addressable key anyway?

[
Key
||
Key <- hb_maps:keys(TrieNode, Opts),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want hb_private:reset here before the keys invocation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants